package p.ithorns.tools.translator.sdk.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import sun.misc.Unsafe;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

/**
 * UnSafeUtil
 *
 * @author HtL
 * @date 2025/2/5 14:05
 * @since 1.0.0
 */
public class UnSafeUtil {

    private final static Logger log = LoggerFactory.getLogger(UnSafeUtil.class);

    private final static String SPRING_ANNOTATION_HANDLER =
            "org.springframework.core.annotation.SynthesizedMergedAnnotationInvocationHandler";

    private final static String SUN_ANNOTATION_HANDLER =
            "sun.reflect.annotation.AnnotationInvocationHandler";

    private final static Map<String, String> ANNOTATION_HANDLER = new HashMap<>(2);
    static {
        ANNOTATION_HANDLER.put(SPRING_ANNOTATION_HANDLER, "valueCache");
        ANNOTATION_HANDLER.put(SUN_ANNOTATION_HANDLER, "memberValues");
    }

    /**
     * 解决 sun.reflect.annotation 为开放问题
     * 就不用在VM参数里添加 --add-opens java.base/sun.reflect.annotation=ALL-UNNAMED
     */
    public static void replaceModule() {
        Unsafe unsafe = UnsafeUtils.getUnsafe();
        try {
            Module module = Object.class.getModule();
            long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
            unsafe.getAndSetObject(UnSafeUtil.class, addr, module);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }


    public static Map<String, Object> getProxyFields(Annotation annotation) {
        InvocationHandler h = Proxy.getInvocationHandler(annotation);
        Class<? extends InvocationHandler> aClass = h.getClass();
        String handlerName = aClass.getName();
        try {
            String cacheName = ANNOTATION_HANDLER.get(handlerName);
            Field valueCache = aClass.getDeclaredField(cacheName);
            valueCache.setAccessible(true);
            return (Map<String, Object>) valueCache.get(h);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return Collections.emptyMap();
    }

    public static String getAnnotationHandler(Class<? extends InvocationHandler> aClass) {
        return ANNOTATION_HANDLER.get(aClass.getName());
    }


    /**
     * 动态更新注解值，用于缓存提升性能
     * 注意该方法仅针对spring对注解的代理有效
     */
    @SuppressWarnings("unchecked")
    public static void updateAnnotationProxy(Annotation annotation, String fieldName, Object value) {
        InvocationHandler h = Proxy.getInvocationHandler(annotation);
        Field field = LambdaUtil.sure(() -> {
            Class<? extends InvocationHandler> aClass = h.getClass();
            String cacheName = UnSafeUtil.getAnnotationHandler(aClass);
            return aClass.getDeclaredField(cacheName);
        });
        if (null != field) {
            // --add-opens java.base/sun.reflect.annotation=ALL-UNNAMED
            field.setAccessible(true);
            Map<String, Object> memberValues = (Map<String, Object>) LambdaUtil.sure(() -> field.get(h));
            memberValues.put(fieldName, value);
        }
    }


    public static String getTypeWithGenerics(Type type) {
        if (type instanceof ParameterizedType parameterizedType) {
            Type rawType = parameterizedType.getRawType();
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

            // Recursively get the generic types
            String generics = String.join(", ",
                    java.util.Arrays.stream(actualTypeArguments)
                            .map(UnSafeUtil::getTypeWithGenerics)
                            .toArray(String[]::new));

            return String.format("%s<%s>", rawType.getTypeName(), generics);
        } else {
            return type.getTypeName();
        }
    }

    public static Field[] getAllFields(final Class<?> cls) {
        assert null != cls;
        final Map<String, Field> fieldMap = new LinkedHashMap<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            for (Field field : currentClass.getDeclaredFields()) {
                // 子类字段优先，自动覆盖父类同名字段
                fieldMap.putIfAbsent(field.getName(), field);
            }
            currentClass = currentClass.getSuperclass();
        }
        return fieldMap.values().toArray(new Field[0]);
    }

}